<?php

// 第三方接口提现订单模型

namespace app\api\model;

use think\Model;
use think\facade\Cache;
use think\facade\Log;
use think\facade\Request;

class PaymentWithdrawalApi extends Model
{

    // 数据表名称,不包括表前缀
    protected $name = 'payment_withdrawal_api';
    // 数据表主键
    protected $pk = 'order_id';
    // 时间字段取出后的默认时间格式
    protected $dateFormat = false;
    // Redis缓存驱动
    public $redis;

    protected function initialize()
    {
        parent::initialize();
        // redis连接缓存驱动
        $this->redis = Cache::store('redis');
        // 文件连接缓存驱动
        // $this->redis =Cache::store('file');
    }

    /**
     * 第三方提现通知相关操作
     * @access public
     * @param  string  $order_id         平台订单号
     * @param  string  $notify_param     通知参数
     * @param  string  $cash             金额
     * @param  string  $status           状态
     * @param  string  $other_order_id   第三方订单号
     * @param  string  $payment_company  第三方总提现公司
     * @param  boolean $verify_rule      验证订单规则
     * @param  boolean $verify_sign      验证sign
     * @param  boolean $all_order_verify 内部绿色通道
     * @return mixed
     */
    public function notifyAbout($order_id = 0, $notify_param = '', $cash = 0, $status = 0, $other_order_id = 0, $payment_company = '', $verify_rule = true, $verify_sign = false, $all_order_verify = true)
    {

        // 查找订单
        $order_arr = self::where(['order_id' => $order_id])->find();
        if ($order_arr) {

            // 内部绿色通道，是否关闭验证（比如模拟第三方通知）
            if ($all_order_verify) {

                $company_config = $this->redis->get($order_arr['child_company']);
                if (!$company_config) {
                    abort(404, "平台订单号是（{$order_arr['order_id']}）：验证第三方提现订单状态，（{$order_arr['child_company']}）提现公司配置错误");
                }

                // 提现公司订单验证开启
                if ($company_config['value']['company_order_verify']) {

                    // $verify_rule为true验证第三方订单，为false验证平台订单号
                    $verify_order_id = $verify_rule ? $other_order_id : $order_id;

                    // 实例化第三方订单模型                
                    $company = $order_arr['company'] . 'WithdrawalQuery';

                    $verify = $this->$company($verify_order_id, $company_config['value'], true, $order_arr);
                    if (!$verify) {
                        abort(404, "平台订单号是（{$order_arr['order_id']}）：提现接口查询异常：（第三方系统异常）第三方提现子公司（{$order_arr['child_company']}），第三方订单号是（{$other_order_id}）");
                    }
                    if ($verify != $status) {
                        abort(404, "平台订单号是（{$order_arr['order_id']}）：提现接口查询异常：查询状态（{$verify}）和通知状态（{$status}）不一样，第三方提现子公司（{$order_arr['child_company']}），第三方订单号是（{$other_order_id}）");
                    }
                }

                // 访问请求ip地址
                $request_ip = Request::instance()->ip();
                if ($company_config['value']['notify_ip']) {
                    if (!in_array($request_ip, $company_config['value']['notify_ip'])) {
                        abort(404, "平台订单号是（{$order_arr['order_id']}）：上游服务端通知IP（{$request_ip}）不在白名单中");
                    }
                }

                // 是否验证sign
                if ($verify_sign) {
                    $company = $order_arr['company'] . 'Sign';
                    $sign = $this->$company($order_id, $company_config['value']);
                    if (!$sign) {
                        abort(404, "平台订单号是（{$order_arr['order_id']}）：验证sign失败");
                    }
                }

                // 是否接收通知
                if ($company_config['value']['notify_open'] != 1) {
                    abort(404, "平台订单号是（{$order_arr['order_id']}）：提现通知关闭");
                }
            }

            // 订单状态：1:处理中 2:成功 3:失败 4:关闭
            switch ($order_arr['status']) {
                    // 处理中
                case '1':
                    // 默认提现订单要在15天内完成
                    if (time() > $order_arr['create_time'] + 86400 * 30) {
                        $remark = "第三方{$order_arr['company']}提现，默认提现订单要在30天内完成";
                    }
                    // 验证公司
                    if ($order_arr['company'] != $payment_company) {
                        $remark = "涉嫌修改第三方提现公司{$order_arr['company']}名称：{$payment_company}";
                    }
                    // 验证金额
                    if ($order_arr['cash'] != intval(strval($cash * 100))) {
                        $remark = "涉嫌修改第三方提现公司{$order_arr['company']}的提现金额：{$cash}";
                    }
                    if (isset($remark)) {
                        self::where(['order_id' => $order_id, 'status' => 1])
                            ->update(['remark' => $remark, 'status' => 4, 'other_order_id' => $other_order_id, 'update_time' => time()]);
                        payment_error_log(650000, $notify_param);
                        abort(404, $remark);
                    }

                    // 第三方可能会在一秒中多次通知相同订单，用redis处理。
                    $order_notify_key = 'order_notify_' . $order_arr['order_id'];
                    $order_notify = $this->redis->has($order_notify_key);
                    if ($order_notify) {
                        $this->redis->inc($order_notify_key, 1);
                        //payment_error_log(650001, $order_arr);
                        abort(404, "{$order_arr['company']}提现，订单通知有误1，原因可能是提现公司在一秒中多次通知相同订单");
                    } else {
                        $this->redis->set($order_notify_key, 1, 5);
                    }
                    $order_notify_value = $this->redis->get($order_notify_key);
                    if (!$order_notify_value || $order_notify_value > 1) {
                        payment_error_log(650002, $order_arr);
                        abort(404, "{$order_arr['company']}提现，订单通知有误2，原因可能是提现公司在一秒中多次通知相同订单");
                    }

                    $payment_order_data = [
                        'status' => $status,
                        'notify_param' => json_encode($notify_param, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
                        'other_order_id' => $other_order_id,
                        'update_time' => time(),
                    ];
                    // 更新提现订单表状态
                    try {
                        $update_data = self::where(['order_id' => $order_id, 'status' => 1])->update($payment_order_data);
                    } catch (\Exception $e) {
                        payment_error_log(650003, $order_arr, $e->getMessage());
                        abort(404, "更新提现订单表状态失败1,{$order_arr['company']}提现");
                    }
                    if ($update_data) {
                        return $this->paySuccess($order_arr, $other_order_id, $status);
                    } else {
                        payment_error_log(650004, $order_arr);
                        abort(404, "更新提现订单表状态失败2,{$order_arr['company']}支付");
                    }

                    // 订单已经提现完成（成功）
                case '2':
                    //payment_error_log(650005, $order_arr, "可能是第三方用了(多台服务器通知)，也有可能是第一次提现公司（{$order_arr['company']}）通知出现异常，状态已经修改，但是没有返回success", false);
                    return 'success';
                    // 订单已经提现完成（失败）
                case '3':
                    //payment_error_log(650005, $order_arr, "可能是第三方用了(多台服务器通知)，也有可能是第一次提现公司（{$order_arr['company']}）通知出现异常，状态已经修改，但是没有返回success", false);
                    return 'success';
                default:
                    // 状态错误
                    abort(404, "订单提现状态错误,现在状态是（{$order_arr['status']}）");
            }
        } else {
            // 订单号不存在
        }
        abort(404, '可能是别人在模拟第三方通知，或者程序出现异常');
    }

    /**
     * 成功相关操作
     * @access public
     * @param  array  $order_arr       订单数组
     * @param  string $other_order_id  第三方订单号
     * @param  string $order_status    状态
     * @return mixed
     */
    public function paySuccess($order_arr = [], $other_order_id = 0, $order_status = 0)
    {

        // 第三方可能会在一秒中多次通知相同订单，用redis处理。
        $order_notify_key = 'merchant_order_notify_' . $order_arr['order_id'];
        $order_notify = $this->redis->has($order_notify_key);
        if ($order_notify) {
            payment_error_log(650006, $order_arr);
            abort(404, "{$order_arr['company']}提现，订单通知有误2，原因可能是提现公司在一秒中多次通知相同订单");
        } else {
            $this->redis->set($order_notify_key, 1, 5);
        }
        // 处理完数据，给第三方提现返回接收成功
        return 'success';
    }

    /**
     * 付易通提现
     * @access public
     * @param  array  $parameters   创建订单参数
     * @param  array  $pay_config   提现配置
     * @return array
     */
    public function fuyitongWithdrawal($parameters = [], $pay_config = [])
    {
        try {

            $arr['mch_id'] = $pay_config['merchant_id'];                  // 分配的商户号
            $arr['amount'] = sprintf('%.2f', $parameters['cash']);        // 金额，单位元 
            $arr['bank_id'] = '';                                         // 银行开户行编码 N
            $arr['notify_url'] = $pay_config['notify_url'];               // 通知接口
            $arr['sn'] = $parameters['order_id'];                         // 当前请求提现的序列号
            $arr['bank_card'] = $parameters['bank_card'];                 // 银行卡号
            $arr['user_name'] = $parameters['name'];                      // 持卡人姓名
            $arr['bank_branch'] = $parameters['account_opening_branch'];  // 开户银行，见附录1里面的银行编号
            $arr['bank_province'] = '';                                   // 开户省份 N
            $arr['bank_city'] = '';                                       // 开户城市 N

            $key = $pay_config['key'];

            $arr = array_filter($arr);
            ksort($arr);

            $string = '';
            foreach ($arr as $k => $v) {
                $string .= $k . '=' . $v . '&';
            }
            $string = $string . 'key=' . $key;
            $arr['sign'] = md5($string);

            $request_url = $pay_config['request_url'];            // 接口请求地址
            $resdata = go_curl($request_url, $arr, 'post', 12);

            Log::record("平台订单号：{$parameters['order_id']}：付易通提现第一次返回结果：{$resdata}", 'error');

            // 一般是第三方超时，结果为空
            if (empty($resdata)) {
                return ['code' => 200, 'other_order_id' => 102, 'msg' => '处理中', 'return_param' => $resdata];
            }
            /*
              Array
              (
              [status] => 0
              [order_id] => 20190318172739653271
              [msg] => 提交成功，保存订单号，请联系商户审核
              )
             */
            $resarray = json_decode($resdata, true);
            // 提示
            $msg = isset($resarray['msg']) ? $resarray['msg'] : '处理中';
            // 第三方订单号
            $other_order_id = $parameters['order_id'];
            // 状态
            $status = isset($resarray['status']) ? $resarray['status'] : 88;

            // 失败：只要不是失败，都认为创建成功
            if ($status == 2) {
                return ['code' => 300, 'other_order_id' => $other_order_id, 'msg' => $msg, 'return_param' => $resdata];
            }

            // 第三方可能 502
            return ['code' => 200, 'other_order_id' => $other_order_id, 'msg' => $msg, 'return_param' => $resdata];
            /*
              Array
              (
              [status] => 2
              [msg] => IP鉴权失败！
              )
             */
        } catch (\Exception $e) {
            return ['code' => 200, 'other_order_id' => 1000, 'msg' => '处理中', 'return_param' => $e->getMessage()];
        }
    }

    /**
     * 付易通提现，验证订单号，是不是已经提现成功
     * @access public
     * @param  string   $order_id     第三方订单号（本地订单号）
     * @param  array    $pay_config   提现配置
     * @param  boolean  $rule         返回类型
     * @param  array    $other_param  其他参数
     * @return mixed
     */
    public function fuyitongWithdrawalQuery($order_id = 0, $pay_config = [], $rule = false, $other_param = [])
    {

        $arr['mchid'] = $pay_config['merchant_id'];            // 分配的商户号
        $arr['cporder'] = $order_id;                           // 提现请求时候的渠道订单号
        $key = $pay_config['key'];

        $arr = array_filter($arr);
        ksort($arr);

        $string = '';
        foreach ($arr as $k => $v) {
            $string .= $k . '=' . $v . '&';
        }
        $string = $string . 'key=' . $key;
        $arr['sign'] = md5($string);

        $request_url = $pay_config['request_url_query'];       // 提现接口请求地址
        $resdata = go_curl($request_url, $arr, 'post', 12);

        Log::record("平台订单号：{$order_id}：付易通提现直接查询返回结果：{$resdata}", 'error');

        $resarray = json_decode($resdata, true);

        // 验证提现订单规则
        if ($rule) {
            if (isset($resarray['status'])) {
                switch ($resarray['status']) {
                    case '1':
                        return 2;  // 成功
                    case '2':
                        return 3;  // 失败
                    default:
                        return false;
                }
            }
            return false;
        }

        if (isset($resarray['status']) && $resarray['status'] == '1') {
            return ['code' => 200, 'return_param' => substr($resdata, 0, 60000), 'data' => json_encode($arr, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)];
        }
        return ['code' => 300, 'return_param' => substr($resdata, 0, 60000), 'data' => json_encode($arr, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)];

        /**
          Array ( [status] => 1 [mchid] => 10000508 [msg] => 查询成功！ [cporder] => 20181107145600571725 )
          Array ( [status] => 202 [msg] => 无提现记录！ )
          Array ( [status] => 0 [mchid] => 10000508 [msg] => 查询成功！ [cporder] => 20181107141535585073 )
          其中status值为1表示提现下发成功，0表示处理中，2表示下发失败
         */
    }

    /**
     * 付易通提现，查询帐号余额
     * @access public
     * @param  array   $pay_config  提现配置
     * @return boolean
     */
    public function fuyitongWithdrawalQueryCash($pay_config = [])
    {

        $arr['mchid'] = $pay_config['merchant_id'];              // 分配的商户号
        $key = $pay_config['key'];

        $arr = array_filter($arr);
        ksort($arr);

        $string = '';
        foreach ($arr as $k => $v) {
            $string .= $k . '=' . $v . '&';
        }
        $string = $string . 'key=' . $key;
        $arr['sign'] = md5($string);

        $request_url = $pay_config['request_url_cash_query'];   // 提现接口请求地址
        $resdata = go_curl($request_url, $arr, 'post', 12);

        Log::record("付易通提现查询帐号余额返回结果：{$resdata}", 'error');

        $resarray = json_decode($resdata, true);
        if (isset($resarray['amount'])) {
            return ['code' => 200, 'return_param' => substr($resdata, 0, 60000), 'cash' => $resarray['amount'], 'data' => json_encode($arr, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)];
        }

        return ['code' => 300, 'return_param' => substr($resdata, 0, 60000), 'cash' => 0, 'data' => json_encode($arr, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)];

        /**
         * Array ( [amount] => 50 [mchid] => 10000508 [msg] => 查询成功！ )
         */
    }

    /**
     * 优聚合提现
     * @access public
     * @param  array  $parameters   创建订单参数
     * @param  array  $pay_config   提现配置
     * @return array
     */
    public function youjuheWithdrawal($parameters = [], $pay_config = [])
    {
        try {

            $arr['mch_id'] = $pay_config['merchant_id'];       // 商户号，由平台分配
            $arr['out_trade_no'] = $parameters['order_id'];    // 商户系统内部的订单号
            $arr['product_type'] = $pay_config['app_id'];      // 当前提现通道编码 app_id
            $arr['bank_city'] = $parameters['city'];           // 收款银行所在城市
            $arr['bank_name'] = $parameters['bank_name'];      // 收款银行名称
            $arr['pay_name'] = $parameters['name'];            // 收款银行账户名
            $arr['pay_card_no'] = $parameters['bank_card'];    // 收款银行卡
            $arr['total_fee'] = sprintf('%.2f', $parameters['cash']);  // 金额
            $arr['body'] = '转账';                             // 代付简要描述
            $arr['pay_type'] = $parameters['account_type'] == '公户' ? 1 : 0;   // 收款账号类型 0：对私 1：对公
            if ($arr['pay_type'] == 1) {
                $arr['bank_union_no'] = $parameters['account_opening_branch'];  // 联行号，Pay_type=1是为必须,可选
            }
            $arr['nonce_str'] = rand(100000, 999999);          // 随机数
            //$arr['op_user_id'] = '';                         // 操作员账号,可选

            array_filter($arr);
            ksort($arr);
            $key = $pay_config['key'];

            $string = '';
            foreach ($arr as $k => $v) {
                $string .= $k . '=' . $v . '&';
            }
            $string = $string . 'key=' . $key;
            $arr['sign'] = strtoupper(md5($string));

            $request_url = $pay_config['request_url'];         // 接口请求地址
            $resdata = go_curl($request_url, json_encode($arr), 'post', 12);

            Log::record("平台订单号：{$parameters['order_id']}：优聚合提现第一次返回结果：{$resdata}", 'error');

            // 一般是第三方超时，结果为空
            if (empty($resdata)) {
                return ['code' => 200, 'other_order_id' => 102, 'msg' => '处理中', 'return_param' => $resdata];
            }
            /*
              Array
              (
              [err_code] =>
              [err_msg] =>
              [mch_id] => 51152
              [message] =>
              [nonce_str] => d73c19d6df7e4fd4921434b1c24a129e
              [result_code] => 0
              [sign] => 8539C335D2BA97CFFEE57CB31783AAFC
              [status] => 0
              [trade_state] => NOTPAY
              [transaction_id] => 2019051509171125535366879
              )
             */
            $resarray = json_decode($resdata, true);
            // 提示
            $msg = isset($resarray['message']) ? $resarray['message'] : '处理中';
            // 第三方订单号
            $other_order_id = isset($resarray['transaction_id']) ? $resarray['transaction_id'] : 100;
            // 状态
            $status = isset($resarray['status']) ? $resarray['status'] : 0;

            // 失败：只要不是失败，都认为创建成功
            if ($status == 1) {
                return ['code' => 300, 'other_order_id' => $other_order_id, 'msg' => $msg, 'return_param' => $resdata];
            }

            // 第三方可能 502
            return ['code' => 200, 'other_order_id' => $other_order_id, 'msg' => $msg, 'return_param' => $resdata];
            /*
              {"status":"1","message":"签名验证失败"}
             */
        } catch (\Exception $e) {
            return ['code' => 200, 'other_order_id' => 1000, 'msg' => '处理中', 'return_param' => $e->getMessage()];
        }
    }

    /**
     * 优聚合提现，验证订单号，是不是已经提现成功
     * @access public
     * @param  string   $order_id     第三方订单号（本地订单号）
     * @param  array    $pay_config   提现配置
     * @param  boolean  $rule         返回类型
     * @param  array    $other_param  其他参数
     * @return mixed
     */
    public function youjuheWithdrawalQuery($order_id = 0, $pay_config = [], $rule = false, $other_param = [])
    {

        $arr['mch_id'] = $pay_config['merchant_id'];           // 商户号
        $arr['out_trade_no'] = $order_id;                      // 商户订单号
        $arr['nonce_str'] = rand(100000, 999999);              // 随机数

        array_filter($arr);
        ksort($arr);
        $key = $pay_config['key'];

        $string = '';
        foreach ($arr as $k => $v) {
            $string .= $k . '=' . $v . '&';
        }
        $string = $string . 'key=' . $key;
        $arr['sign'] = strtoupper(md5($string));

        $request_url = $pay_config['request_url_query'];       // 提现接口请求地址
        $resdata = go_curl($request_url, json_encode($arr), 'post', 12);

        Log::record("平台订单号：{$order_id}：付易通提现直接查询返回结果：{$resdata}", 'error');

        $resarray = json_decode($resdata, true);

        // 验证提现订单规则
        if ($rule) {
            if (isset($resarray['trade_state'])) {
                switch ($resarray['trade_state']) {
                    case 'SUCCESS':
                        return 2;  // 成功
                    case 'PAYERROR':
                        return 3;  // 失败
                    default:
                        return false;
                }
            }
            return false;
        }

        if (isset($resarray['trade_state']) && $resarray['trade_state'] == 'SUCCESS') {
            return ['code' => 200, 'return_param' => substr($resdata, 0, 60000), 'data' => json_encode($arr, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)];
        }
        return ['code' => 300, 'return_param' => substr($resdata, 0, 60000), 'data' => json_encode($arr, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)];

        /**
          Array
          (
          [err_code] =>
          [err_msg] =>
          [mch_id] => 51152
          [message] =>
          [nonce_str] => 4c82ca53e3634ae0b64e030fb23932bd
          [result_code] => 0
          [sign] => 72F7F1EA3084D63A9A5C3AF67D0F42E2
          [status] => 0
          [trade_state] => PAYERROR
          )
          NOTPAY—正在调拨资金,请耐心等待
          PROCESSING—处理中,请耐心等待划账
          SUCCESS—出款成功,请检查款项是否到账
          REVOKED—撤销代付请求,款项被退回
          PAYERROR—代付系统错误,款项被退回
          CLOSE—代付系统异常，订单已关闭
         */
    }

    /**
     * 优聚合提现，查询帐号余额
     * @access public
     * @param  array   $pay_config  提现配置
     * @return boolean
     */
    public function youjuheWithdrawalQueryCash($pay_config = [])
    {

        $arr['mch_id'] = $pay_config['merchant_id'];            // 商户号
        $arr['product_type'] = $pay_config['app_id'];           // 当前提现通道编码 app_id
        $arr['nonce_str'] = rand(100000, 999999);               // 随机数

        array_filter($arr);
        ksort($arr);
        $key = $pay_config['key'];

        $string = '';
        foreach ($arr as $k => $v) {
            $string .= $k . '=' . $v . '&';
        }
        $string = $string . 'key=' . $key;
        $arr['sign'] = strtoupper(md5($string));

        $request_url = $pay_config['request_url_cash_query'];   // 提现接口请求地址
        $resdata = go_curl($request_url, json_encode($arr), 'post', 12);

        Log::record("优聚合提现查询帐号余额返回结果：{$resdata}", 'error');

        $resarray = json_decode($resdata, true);
        if (isset($resarray['current_amount'])) {
            return ['code' => 200, 'return_param' => substr($resdata, 0, 60000), 'cash' => $resarray['current_amount'], 'data' => json_encode($arr, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)];
        }

        return ['code' => 300, 'return_param' => substr($resdata, 0, 60000), 'cash' => 0, 'data' => json_encode($arr, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)];

        /**
          Array
          (
          [can_settle_balance] => 323.08
          [current_amount] => 23638.03
          [err_code] =>
          [err_msg] =>
          [frozen_amount] => 0.00
          [mch_id] => 51152
          [message] =>
          [nonce_str] => 5441c6be08be458a976eb44f0c16029c
          [result_code] => 0
          [sign] => 81DB1077433D71DEBAEF0D0124283E8D
          [status] => 0
          )
         * 
         * 
         * 
          can_settle_balance	可结算余额	Money	是	当前可结算的余额总数	2000.00
          current_amount	可用余额	Money	是	当前可用的余额总数	10000.00
          frozen_amount      	冻结余额	Money	是	当前冻结的余额总数	5000.00
         */
    }
}
